package edu.tec.mty.core.modules;

import java.util.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Pattern;

import edu.tec.mty.extras.Vector2D;
import edu.tec.mty.core.modules.models.*;
import edu.tec.mty.extras.*;

public class World { // extends Thread {
	
	private String teamName;
	private volatile WorldModelFactory worldModelFactory = new WorldModelFactory();
	private BallModelFactory ballModelFactory = new BallModelFactory();
	private char agentType;
//	private boolean running = true;
	private final Lock lock = new ReentrantLock();
//	private final Condition update = lock.newCondition();
//	private boolean updating = false;
//	private boolean closing = false;
	private Self player;
	private long step = 0;
	private long lastStepUpdated = 0;
	private Vector<Pointer<String, String>> members = new Vector<Pointer<String, String>>();
	private String ballInfo = "";
	private Pattern actionPattern = Pattern.compile("(t|k)");
	public static long GAME_TIMESTAMP = 0;
	
	synchronized public static void setGameTimestamp(long time) {
		GAME_TIMESTAMP = time;
	}
	
	synchronized public static long getGameTimestamp() {
		return GAME_TIMESTAMP;
	}
	
	public World(String teamName, int number, char agentType, Self player) {
		this.teamName = teamName;
		this.agentType = agentType;
		this.player = player;
	}

	public WorldModelFactory getWorldModelFactory() {
		return this.worldModelFactory;
	}

	public BallModelFactory getBallModelFactory() {
		return this.ballModelFactory;
	}
	
//	private void coachProcessInformation() {
//		//Vector<Pointer<String,String>> worldInfo;
//	}
	
	private void playerProcessInformation() {
//		Vector<Member> allMatesOnThisCycle = new Vector<Member>();
//		Vector<Member> allOpptsOnThisCycle = new Vector<Member>();
//		Vector<Member> otherPlayersOnThisCycle = new Vector<Member>();
		Vector<Player> allPlayersOnThisCycle = new Vector<Player>();
		Position position = this.player.getPositionAtTime(lastStepUpdated);
		if (position == null) {
			position = this.player.getPosition();
		}
		Vector2D speed = Vector2D.getVectorWithMagnitudeAndDirection(Self.SPEED_BUFFER.getAmountOfSpeedAtTime(lastStepUpdated),
				 Self.SPEED_BUFFER.getDirectionOfSpeedAtAtime(lastStepUpdated));
		Ball ball;
		for (int i = 0; i < this.members.size(); i++) {
			String memberDescription = this.members.elementAt(i).getKey();
			String[] description = memberDescription.split("\\s");
			String playerInfo = this.members.elementAt(i).getObject();
			String[] parameters = playerInfo.split(" ");
			double direction = 0.0, bodyFacingDir = 0.0, headFacingDir = 0.0;
			double distance = 60.0, distChange = 0.0, dirChange = 0.0;
			double pointdir = 0.0;
			String action = "";
			boolean pointing = false, kicking = false, tackling = false;
			switch (parameters.length) {
			case 1:
				direction = Double.parseDouble(parameters[0]);
				break;
			case 2:
				distance = Double.parseDouble(parameters[0]);
				direction = Double.parseDouble(parameters[1]);
				break;
			case 3:
				if (this.actionPattern.matcher(parameters[2]).matches()) {			
					distance = Double.parseDouble(parameters[0]);
					direction = Double.parseDouble(parameters[1]);
					action = parameters[2];
					if (action.equals("t")) {
						tackling = true;
					} else {
						kicking = true;
					}
				} else {
					distance = Double.parseDouble(parameters[0]);
					direction = Double.parseDouble(parameters[1]);
					pointdir = Double.parseDouble(parameters[2]);
					pointing = true;
				}
				break;
			case 4:
				if (this.actionPattern.matcher(parameters[3]).matches()) {
					distance = Double.parseDouble(parameters[0]);
					direction = Double.parseDouble(parameters[1]);
					pointdir = Double.parseDouble(parameters[2]);
					pointing = true;
					action = parameters[3];
					if (action.equals("t")) {
						tackling = true;
					} else {
						kicking = true;
					}
				} else {
					distance = Double.parseDouble(parameters[0]);
					direction = Double.parseDouble(parameters[1]);
					distChange = Double.parseDouble(parameters[2]);
					dirChange = Double.parseDouble(parameters[3]);
				}
				break;
			case 5:
				if (this.actionPattern.matcher(parameters[4]).matches()) {	
					distance = Double.parseDouble(parameters[0]);
					direction = Double.parseDouble(parameters[1]);
					distChange = Double.parseDouble(parameters[2]);
					dirChange = Double.parseDouble(parameters[3]);
					action = parameters[4];
					if (action.equals("t")) {
						tackling = true;
					} else {
						kicking = true;
					}
				} else {
					distance = Double.parseDouble(parameters[0]);
					direction = Double.parseDouble(parameters[1]);
					distChange = Double.parseDouble(parameters[2]);
					dirChange = Double.parseDouble(parameters[3]);
					pointdir = Double.parseDouble(parameters[4]);
					pointing = true;
				}
				break;
			case 6:
				if (this.actionPattern.matcher(parameters[5]).matches()) {
					distance = Double.parseDouble(parameters[0]);
					direction = Double.parseDouble(parameters[1]);
					distChange = Double.parseDouble(parameters[2]);
					dirChange = Double.parseDouble(parameters[3]);
					pointdir = Double.parseDouble(parameters[4]);
					pointing = true;
					action = parameters[5];
					if (action.equals("t")) {
						tackling = true;
					} else {
						kicking = true;
					}
				} else {
					distance = Double.parseDouble(parameters[0]);
					direction = Double.parseDouble(parameters[1]);
					distChange = Double.parseDouble(parameters[2]);
					dirChange = Double.parseDouble(parameters[3]);
					bodyFacingDir = Double.parseDouble(parameters[4]);
					headFacingDir = Double.parseDouble(parameters[5]);
				}
				break;
			case 7:
				if (this.actionPattern.matcher(parameters[6]).matches()) {
					distance = Double.parseDouble(parameters[0]);
					direction = Double.parseDouble(parameters[1]);
					distChange = Double.parseDouble(parameters[2]);
					dirChange = Double.parseDouble(parameters[3]);
					bodyFacingDir = Double.parseDouble(parameters[4]);
					headFacingDir = Double.parseDouble(parameters[5]);
					action = parameters[6];
					if (action.equals("t")) {
						tackling = true;
					} else {
						kicking = true;
					}
				} else {
					distance = Double.parseDouble(parameters[0]);
					direction = Double.parseDouble(parameters[1]);
					distChange = Double.parseDouble(parameters[2]);
					dirChange = Double.parseDouble(parameters[3]);
					bodyFacingDir = Double.parseDouble(parameters[4]);
					headFacingDir = Double.parseDouble(parameters[5]);
					pointdir = Double.parseDouble(parameters[6]);
					pointing = true;
				}
				break;
			case 8:
				distance = Double.parseDouble(parameters[0]);
				direction = Double.parseDouble(parameters[1]);
				distChange = Double.parseDouble(parameters[2]);
				dirChange = Double.parseDouble(parameters[3]);
				bodyFacingDir = Double.parseDouble(parameters[4]);
				headFacingDir = Double.parseDouble(parameters[5]);
				pointdir = Double.parseDouble(parameters[6]);
				pointing = true;
				action = parameters[7];
				if (action.equals("t")) {
					tackling = true;
				} else {
					kicking = true;
				}
				break;
			default:
				break;
			}
			Player member = null;
			int uniformNumber = 0;
			String teamName = "";
			switch (description.length) {
			case 1:
				member = new Player(position, speed, this.step, direction, distance, distChange, dirChange, bodyFacingDir, headFacingDir);
				if (pointing) {
					member.setPointTo(pointdir);
				}
				if (tackling) {
					member.setTackling();
				}
				if (kicking) {
					member.setKicking();
				}
//				otherPlayersOnThisCycle.add(member);
				break;
			case 2:
				teamName = description[1];
				member = new Player(position, speed, this.step, direction, distance, distChange, dirChange, bodyFacingDir, headFacingDir);
				if (pointing) {
					member.setPointTo(pointdir);
				}
				if (tackling) {
					member.setTackling();
				}
				if (kicking) {
					member.setKicking();
				}
				if (teamName.equals(this.teamName)) {
					member.setTeam("mate");
//					allMatesOnThisCycle.add(member);
				} else {
					member.setTeam("opponent");
//					allOpptsOnThisCycle.add(member);
				}
				break;
			case 3:
				teamName = description[1];
				uniformNumber = Integer.parseInt(description[2]);
				if (teamName.equals(this.teamName)) {
					member = new Player(position, speed, this.step, direction, distance, distChange, dirChange, bodyFacingDir, headFacingDir);
					if (pointing) {
						member.setPointTo(pointdir);
					}
					if (tackling) {
						member.setTackling();
					}
					if (kicking) {
						member.setKicking();
					}					
					member.setTeam("mate");
					member.setUniformNumber(uniformNumber);
//					allMatesOnThisCycle.add(member);
				} else {
					member = new Player(position, speed, this.step, direction, distance, distChange, dirChange, bodyFacingDir, headFacingDir);
					if (pointing) {
						member.setPointTo(pointdir);
					}
					if (tackling) {
						member.setTackling();
					}
					if (kicking) {
						member.setKicking();
					}					
					member.setTeam("opponent");
					member.setUniformNumber(uniformNumber);
//					allOpptsOnThisCycle.add(member);
				}
				break;
			case 4:
				teamName = description[1];
				uniformNumber = Integer.parseInt(description[2]);
				if (teamName.equals(this.teamName)) {
					member = new Player(position, speed, this.step, direction, distance, distChange, dirChange, bodyFacingDir, headFacingDir);
					if (pointing) {
						member.setPointTo(pointdir);
					}
					if (tackling) {
						member.setTackling();
					}
					if (kicking) {
						member.setKicking();
					}
					member.setTeam("mate");
					member.setUniformNumber(uniformNumber);
//					allMatesOnThisCycle.add(member);
					
				} else {
					member = new Player(position, speed, this.step, direction, distance, distChange, dirChange, bodyFacingDir, headFacingDir);
					if (pointing) {
						member.setPointTo(pointdir);
					}
					if (tackling) {
						member.setTackling();
					}
					if (kicking) {
						member.setKicking();
					}
					member.setTeam("opponent");
					member.setUniformNumber(uniformNumber);
//					allOpptsOnThisCycle.add(member);
				}
				break;
			}
			allPlayersOnThisCycle.add(member);
		}
		if (!this.ballInfo.equals("")) {
			String[] parameters = this.ballInfo.split("\\s");
			double direction = 0.0, distance = 60.0; 
			double distChange = 0.0, dirChange = 0.0;
			switch (parameters.length) {
			case 1:
				direction = Double.parseDouble(parameters[0]);
				break;
			case 2:
				distance = Double.parseDouble(parameters[0]);
				direction = Double.parseDouble(parameters[1]);
				break;
			case 4:
				distance = Double.parseDouble(parameters[0]);
				direction = Double.parseDouble(parameters[1]);
				distChange = Double.parseDouble(parameters[2]);
				dirChange = Double.parseDouble(parameters[3]);
				break;
			default:
				break;
			}
			ball = new Ball(position, this.step, direction, distance, distChange, dirChange);
		} else {
			ball = new Ball(this.step);
		}
		this.ballModelFactory.updateBall(ball);
		this.worldModelFactory.updateAllPlayers(allPlayersOnThisCycle);
		//this.worldModelFactory.updateAllPlayers(otherPlayersOnThisCycle, allMatesOnThisCycle, allOpptsOnThisCycle);
	}
	
	public void setGlobalTime(long step) {
		this.step = step;
	}
	
	public void setWorldInfo(Vector<Pointer<String, String>> members, String ball, long step) {
		this.lock.lock();
		if (step > this.lastStepUpdated) {
			this.members.clear();
			this.members = members;
			this.ballInfo = ball;
//			this.updating = true;
//			this.update.signal();
			this.lastStepUpdated = step;
			this.playerProcessInformation();
		}
		this.lock.unlock();
	}
	
//	public void shutdown() {
//		this.lock.lock();
//		this.running = false;
//		this.closing = true;
//		this.updating = true;
//		this.update.signal();
//		this.lock.unlock();
//	}
	
//	@Override
//	public void run() {
//		while(this.running) {
//			this.lock.lock();
//			try {
//				while(!this.updating) {
//					this.update.await();
//				}
//			} catch (Exception e) {
//				System.err.println(e);
//			}
//			//Here I update the worldModel
//			if (!this.closing) {
//				if (this.agentType == 'p' || this.agentType == 'g') {
//					this.playerProcessInformation();
//				} else {
//					this.coachProcessInformation();
//				}
//			}
//			//finish the update
//			this.updating = false;
//			this.lock.unlock();
//		}
//		System.out.println("World closed");
//	}
	
	//Test
	
	/*public static void main(String[] args) {
		
	}/**/
	
}
